﻿using System;
using System.Collections.Generic;

namespace Red.Toolkit.Profiler.Logic
{
    public class ProfilerThread
    {
        private string _Name;
        private uint _ID;
        private SortedList<ulong, RawEvent> _Events;

        public ProfilerThread( string name, uint id )
        {
            _ID = id;
            _Name = name;
            _Events = new SortedList<ulong, RawEvent>();
        }

        public string Name { get { return _Name; } }
        public uint ID { get { return _ID; } }
        public SortedList<ulong, RawEvent> Events { get { return _Events; } }

        public void AddEvent( RawEvent evt )
        {
            _Events.Add( evt.ListTime, evt );
        }
    }

    public class ProfilerData
    {
        private List<ProfilerThread> _Threads;
        private SortedList<ulong, RawEvent> _Events;
        private ulong _TimeBase;
        private ulong _TimeFreq;
        private double _TimeLength;
        private string _FilePath;
        private double _MinTime;
        private double _MaxTime;

        public ulong EngineInstance;
        public Dictionary<uint, ProfilerDefinition> _Blocks;
        public Dictionary<uint, ProfilerDefinition> _Signals;
        public Dictionary<uint, ProfilerDefinition> _Counters;
        public Dictionary<uint, string> _Strings;

        public ProfilerData( string filePath, ulong timeBase, ulong timeFreq, ulong engineInstanceId )
        {
            _FilePath = filePath;

            _Blocks = new Dictionary<uint, ProfilerDefinition>();
            _Signals = new Dictionary<uint, ProfilerDefinition>();
            _Counters = new Dictionary<uint, ProfilerDefinition>();
            _Strings = new Dictionary<uint, string>();

            _Threads = new List<ProfilerThread>();
            _Events = new SortedList<ulong, RawEvent>();
            _TimeBase = timeBase;
            _TimeFreq = timeFreq;
            _TimeLength = 0.0;
            _MinTime = double.MaxValue;
            _MaxTime = double.MinValue;

            EngineInstance = engineInstanceId;
        }

        public SortedList<ulong, RawEvent> Events { get { return _Events; } }
        public IList<ProfilerThread> Threads { get { return _Threads; } }
        public string FilePath { get { return _FilePath; } }
        public double Length { get { return _TimeLength; } }
        public Dictionary<uint, ProfilerDefinition> Blocks { get { return _Blocks; } }
        public Dictionary<uint, ProfilerDefinition> Signals { get { return _Signals; } }
        public Dictionary<uint, ProfilerDefinition> Counters { get { return _Counters; } }
        public Dictionary<uint, string> Strings { get { return _Strings; } }
        public double MinTime { get { return _MinTime; } }
        public double MaxTime { get { return _MaxTime; } }

        public string GetString( uint hash )
        {
            if ( !_Strings.ContainsKey( hash ) )
                return "Unknown";

            return _Strings[hash];
        }

        public ProfilerThread AddThread( uint id )
        {
            foreach ( ProfilerThread t in _Threads )
            {
                if ( t.ID == id )
                    return t;
            }

            string name = "Thread " + id.ToString();
            ProfilerThread thread = new ProfilerThread( name, id );
            _Threads.Add( thread );
            return thread;
        }

        public void AddEvent( RawEvent evt )
        {
            // update event time
            evt.ComputeTime( _TimeBase, _TimeFreq );
            _MinTime = Math.Min( _MinTime, evt.StartTime );
            _MaxTime = Math.Max( _MaxTime, evt.EndTime );

            // insert into global list to ensure uniques of time events
            while ( _Events.ContainsKey( evt.ListTime ) )
            {
                evt.AdjustTime();
            }

            // add to lists
            _Events.Add( evt.ListTime, evt );

            // add to thread
            if ( evt.Thread != null )
            {
                evt.Thread.AddEvent( evt );
            }

            // measure time
            if ( evt.EndTime > _TimeLength )
                _TimeLength = evt.EndTime;
        }
    }
}
